#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "vm.h"


/*Loads a program into the brain10 vm. Once started,
  the instructions and data are loaded into memory.
  @param *vm pointer to a brain10 vm struct
  @param fileName the name of the file to load
  */
FILE* loadProgram(brain10 *vm, char fileName[]){

	int ic = 0;

	/* Find and open the file */
	FILE* file = fopen(fileName, "r");
	if(!file){
		printf("Could not open file\n");
		exit(1);
	}
	
	/* the first line should be BRAIN10 */
	char buffer[100];
	fgets(buffer,100,file);
	if(strncmp(buffer, "BRAIN10",7)){
		printf("The file format is not correct\n");
		printf("First line is %s, should be BRAIN10\n",buffer);
		exit(1);
	}
	
	/* all lines up until DATA are instructions. Read
	   them into memory */
	fgets(buffer, 100, file);
	while(strncmp(buffer, "DATA", 4) != 0)
    { 
      strncpy(vm->memory[ic], buffer,4);
      fgets(buffer, 100, file);
      ic++;
    }    
	
	printf("Found %i instructions\n",ic);
	
	return file;
}

/*Dump the contents of the register to the screen
  @param *vm pointer to a brain10 vm struct
  @param n max words to print
  */
void print(brain10 *vm, int n){	
	/*print out the registers*/

	printf("R   :    %s      ", vm->R);
	printf("SP  :    %s\n", vm->SP);
	printf("IC  :    %s        ", vm->IC);
	printf("C   :    %c\n\n", vm->C);

}

/*load instruction
  @param *vm pointer to a virutal machine struct
  @param address address in memory between 0 and 99
  */
void LR(brain10 *vm, int address){
	strncpy(vm->R, vm->memory[address], 4);
}

/*load instruction, low order bits
  @param *vm pointer to a virutal machine struct
  @param address address in memory between 0 and 99
  */
void LL(brain10 *vm, int address){
	int i;
	for(i = 2; i < 4; i++){
		vm->R[i] = vm->memory[address][i];
	}
}

/*load instruction, high order bits
  @param *vm pointer to a virutal machine struct
  @param address address in memory between 0 and 99
  */
void LH(brain10 *vm, int address){
	int i;
	for(i = 0; i < 2; i++){
		vm->R[i] = vm->memory[address][i];
	}
}


/*Copy the contents of the register into
  address.
  @param *vm pointer to a virutal machine struct
  @param address address in memory between 0 and 99
  */
void SR(brain10 *vm, int address){
	strncpy(vm->memory[address], vm->R, 4);
}

/*Copy the contents of the register into SP
  @param *vm pointer to a virutal machine struct
  */
void SP(brain10 *vm){
	strncpy(vm->SP, vm->R, 4);
}

/*Copy the contents of SP into the register
  @param *vm pointer to a virutal machine struct
  */
void PS(brain10 *vm){
	strncpy(vm->R, vm->SP, 4);
}

/*Increment SP, then set the contents of the address
  SP contains to the contents of R
  @param *vm pointer to a virutal machine struct
  */
void PH(brain10* vm){
	int sp = atoi(vm->SP);
	sp++;
	snprintf(vm->SP, 5,"%.4i",sp); //ADDED .4, changed 5
	
	strncpy(vm->memory[sp], vm->R, 4);
}

/*Set R to the contents of the address SP contains,
  then decrement SP.
  @param *vm pointer to a virutal machine struct
  */
void PP (brain10* vm){
	int sp = atoi(vm->SP);
	
	strncpy(vm->R, vm->memory[sp], 4);

	sp--;
	snprintf(vm->SP, 4, "%i",sp);
}

/*If R is equal to the contents of address
  then set C to 'T', else 'F'
  @param *vm pointer to a virutal machine struct
  @param address address in memory between 0 and 99
  */
void CE(brain10 *vm, int address){

	if( strncmp(vm->R, vm->memory[address], 4)){
		vm->C = 'F';
	}else{
		vm->C = 'T';
	}
}

/*If R is less than the contents of address
  then set C to 'T', else 'F'
  @param *vm pointer to a virutal machine struct
  @param address address in memory between 0 and 99
  */
void CL(brain10 *vm, int address){

	int i,j;
	i = atoi(vm->R);
	j = atoi(vm->memory[address]);
		
	if( i < j ){
		vm->C = 'T';
	}else{
		vm->C = 'F';
	}
}

/*If C is 'T' then IC is set to arg
  @param *vm pointer to a virutal machine struct
  @param arg two byte array that contains the arguments
  x1x2 in the initial instruction.
  */
int BT(brain10 *vm, char arg[]){
	if( vm->C == 'T'){
		strncpy(vm->IC, arg, 3);
		return 1;
	}
	return 0;
}

/*Set IC to arg
  @param *vm pointer to a virutal machine struct
  @param arg two byte array that contains the arguments
  x1x2 in the initial instruction.
  */
void BU(brain10 *vm, char arg[]){
	strncpy(vm->IC, arg, 3);
}

/*Reads the next line from the input stream.
  @param *vm pointer to a brain10 vm struct
  @param arg two byte array that contains the arguments
  x1x2 in the initial instruction
  */
void GD(brain10 *vm, int address, FILE *file){
	char buffer[100], temp[5]; //I changed temp to a char instead of an int so that the
	int counter = 0, c, i;	  //the strncpy would stop throwing warnings at us
	fgets(buffer,100,file);

	for(c = address; c < address+10; c++){
		for(i = 0; i < 5; i++){
			temp[i] = buffer[counter];
			counter++;
		}
		strncpy(vm->memory[c],temp,5);
		vm->memory[c][4] = '\0';
	}
}

/*Print the contents of memory 10 at a time.
  @param *vm pointer to a brain10 vm struct
  @param start the location to start printing
  */
void PD(brain10 *vm, int start){

	int i;
	for(i = start; i < start+10; i++){
		printf("Location %i: ",i);
		int j;
		for(j = 0; j < 4; j++){
			printf("%c",vm->memory[i][j]);
		}
		printf("\n");
	}

}


/*Add the contents in the register and the memory at
  paramater location 'address' and then store it back
  into the Register
  @param *vm pointer to a brain10 vm struct
  @param address address in memory between 0 and 99
  */
void AD(brain10 *vm, int address){

	int r, a;
	a = atoi(vm->memory[address]);
	r = atoi(vm->R);
	a += r;
	snprintf(vm->R, 5, "%.4i", a);
}

/*Subtract the contents of the memory at the address
  location only if the Register is larger than the 
  value in the memory and load that back into the Register
  @param *vm pointer to a brain10 vm struct
  @param address address in memory between 0 and 99
  */
void SU(brain10 *vm, int address){

	int r, a;
	a = atoi(vm->memory[address]);
	r = atoi(vm->R);
	if(r >= a){
	 r = r - a;
	 snprintf(vm->R, 5, "%.4i", r);
	}
}

/*Multiply the contents in the register and the memory at
  paramater location 'address' and then store it back
  into the Register
  @param *vm pointer to a brain10 vm struct
  @param address address in memory between 0 and 99
  */
void MU(brain10 *vm, int address){

	int r, a;
	a = atoi(vm->memory[address]);
	r = atoi(vm->R);
	a *= r;
	snprintf(vm->R, 5, "%.4i", a);
}

/*Divide the contents in the register by the value in memory at
  paramater location 'address' and then store it back
  into the Register
  @param *vm pointer to a brain10 vm struct
  @param address address in memory between 0 and 99
  */
void DI(brain10 *vm, int address){

	int r, a;
	a = atoi(vm->memory[address]);
	r = atoi(vm->R);
	if(a > 0){
	 r = r / a;
	 snprintf(vm->R, 5, "%.4i", r);
	}
}

//Add Stack
void AS(brain10 *vm){

	//	int Temp = atoi(vm->SP); //wrong
	//	sprintf(vm->SP, "%d",atoi(vm->SP)-1);
	//	sprintf(vm->SP, "%d",atoi(vm->SP)+Temp);

	int Temp = atoi(vm->memory[atoi(vm->SP)]);
	sprintf(vm->SP, "%.4i",atoi(vm->SP)-1);
	sprintf(vm->memory[atoi(vm->SP)], "%.4i",atoi(vm->memory[atoi(vm->SP)])+Temp);
	
}

//Subtract Stack
void SS(brain10 *vm){
	int Temp = atoi(vm->memory[atoi(vm->SP)]);
	sprintf(vm->SP, "%.4i",atoi(vm->SP)-1);
	sprintf(vm->memory[atoi(vm->SP)], "%.4i",atoi(vm->memory[atoi(vm->SP)])-Temp);
}

//Multiply Stack
void MS(brain10 *vm){
	int Temp = atoi(vm->memory[atoi(vm->SP)]);
	sprintf(vm->SP, "%.4i",atoi(vm->SP)-1);
	sprintf(vm->memory[atoi(vm->SP)], "%.4i",atoi(vm->memory[atoi(vm->SP)])*Temp);
}

//Divide Stack
void DS(brain10 *vm){
	int Temp = atoi(vm->memory[atoi(vm->SP)]);
	sprintf(vm->SP, "%.4i",atoi(vm->SP)-1);
	sprintf(vm->memory[atoi(vm->SP)], "%.4i",atoi(vm->memory[atoi(vm->SP)])/Temp);
}

